﻿
#include "clist.h"
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>

#pragma warning(disable: 4996 4311 4312)


#define PROJECT_TITLE "学生成绩管理系统 " PROJECT_VERSION

typedef struct student {
    unsigned long   number;
    char            name[15];
    float          *scores;
}student, *pstudent;

int g_courses;
const char* g_filename = "record.dat";

const char* g_menu[] = {
    "------------------------------------------------------------",
    " 1. Input record",
    " 2. Caculate total and average score of every course",
    " 3. Caculate total and average score of every student",
    " 4. Sort in descending order by total score of every student",
    " 5. Sort in ascending order by total score of every student",
    " 6. Sort in ascending order by number",
    " 7. Sort in dictionary order by name",
    " 8. Search by number",
    " 9. Search by name",
    "10. Statistic analysis for every course",
    "11. List record",
    "12. Write to a file",
    "13. Read from a file",
    " 0. Exit",
};


void init(clist *pcl);
void uninit(clist *pcl);

// main menu
int get_menu_choice();
// test record
void test_input_record(clist* pcl);
// 1. Input record
void input_record(clist *pcl);
// 2. Caculate total and average score of every course
// 3. Caculate total and average score of every student
void show_all(clist* pcl, flag_t course_average, flag_t student_average);
void show_header(flag_t student_average);
void show_single(pstudent p, flag_t student_average);
// 4. Sort in descending order by total score of every student
// 5. Sort in ascending order by total score of every student
void sort_by_total_score(clist* pcl, flag_t ascending);
flag_t sort_descend_by_total_score_process_handler(const void* const data1, const void* const data2);
flag_t sort_ascend_by_total_score_process_handler(const void* const data1, const void* const data2);
// 6. Sort in ascending order by number
void sort_by_number(clist* pcl);
flag_t sort_ascend_by_number_process_handler(const void* const data1, const void* const data2);
// 7. Sort in dictionary order by name
void sort_by_name(clist* pcl);
flag_t sort_by_name_process_handler(const void* const data1, const void* const data2);
// 8. Search by number
void search_by_number(clist* pcl);
flag_t search_by_number_process_handler(const void* const data1, const void* const data2);
// 9. Search by name
void search_by_name(clist* pcl);
flag_t search_by_name_process_handler(const void* const data1, const void* const data2);
//10. Statistic analysis for every course
void statistic_analysis_courses(clist* pcl);
//11. List record
// Same As 3. Caculate total and average score of every student
//12. Write to a file
void write_to_file(clist* pcl, const char* filename);
//13. Read from a file
void read_from_file(clist* pcl, const char* filename);
// 0. Exit

int main(int argc, char* argv[])
{
    int choice = 0;
    clist cl;

    init(&cl);

    while(choice = get_menu_choice())
    {
        switch (choice)
        {
        case 1:
            input_record(&cl);
            //test_input_record(&cl);
            break;
        case 2:
            show_all(&cl, true, false);
            break;
        case 3:
            show_all(&cl, false, true);
            break;
        case 4:
            sort_by_total_score(&cl, false);
            break;
        case 5:
            sort_by_total_score(&cl, true);
            break;
        case 6:
            sort_by_number(&cl);
            break;
        case 7:
            sort_by_name(&cl);
            break;
        case 8:
            search_by_number(&cl);
            break;
        case 9:
            search_by_name(&cl);
            break;
        case 10:
            statistic_analysis_courses(&cl);
            break;
        case 11:
            show_all(&cl, false, true);
            break;
        case 12:
            write_to_file(&cl, g_filename);
            break;
        case 13:
            read_from_file(&cl, g_filename);
            break;
        }
    }

    uninit(&cl);

    return 0;
}

void init(clist *pcl)
{
    clist_create(pcl);
}

void uninit(clist *pcl)
{
    int i = 0;
    clist_iterator_t it;
    it = clist_begin(pcl);
    for (; it && it != clist_iterator_end; it = clist_step(it, increase, pcl))
    {
        pstudent p = clist_get(it);
        free(p->scores);
    }
    clist_destroy(pcl);
}

int get_menu_choice()
{
    int i = 0;

    printf("============================================================\n");
    printf("%30s%s\n", "", PROJECT_TITLE);
    for (i = 0; i < sizeof(g_menu) / sizeof(g_menu[0]); ++i)
    {
        printf("%s\n", g_menu[i]);
    }
    printf("============================================================\n");

    while (1) {
        printf("Please enter your choice:");
        scanf("%d", &i);

        if (i < 0 || i > 13) printf("Invalid choice!!");
        else break;
    }

    return i;
}

void test_input_record(clist* pcl)
{
    static int base = 1000;
    int i = 0, j = 0, nums = 5;
    g_courses = 6;
    for (; i < nums; ++i, ++base)
    {
        pstudent pst = (pstudent)malloc(sizeof(student));
        pst->number = base;
        sprintf(pst->name, "Coco.Liu-%lu", pst->number);
        pst->scores = (float*)malloc(sizeof(float)* g_courses);
        for (j = 0; j < g_courses; ++j)
        {
            pst->scores[j] = rand() / (float)(RAND_MAX / 100);
        }

        clist_add(pcl, (void*)pst, false);
    }
}

void input_record(clist *pcl)
{
    int i = 0, j = 0, nums = 0;

    printf("How many students you wanna input:");
    scanf("%d", &nums);
    if (g_courses == 0)
    {
        printf("How many courses of every student you wanna input:");
        scanf("%d", &g_courses);
    }
    for (i = 1; i <= nums; ++i)
    {
        pstudent pst = (pstudent)malloc(sizeof(student));
        pst->scores = (float*)malloc(sizeof(float) * g_courses);

        printf("Please input number and name of <%dth> student\n# ", i);
        scanf("%lu %s", &pst->number, pst->name);
        printf("Please input %d scores of student number <%lu>\n# ", g_courses, pst->number);
        for (j = 0; j < g_courses; ++j)
        {
            scanf("%f", pst->scores + j);
        }

        clist_add(pcl, (void*)pst, false);
    }
}

void show_all(clist* pcl, flag_t course_average, flag_t student_average) {
    int i = 0;
    float *course_total = NULL;
    clist_iterator_t it;

    course_total = (float *)malloc(sizeof(float) * g_courses);
    memset(course_total, 0, sizeof(float) * g_courses);

    show_header(student_average);
    
    it = clist_begin(pcl);
    for (; it && it != clist_iterator_end; it = clist_step(it, increase, pcl))
    {
        float student_total = 0.0;
        pstudent p;

        p = clist_get(it);

        printf("%15lu %15s ", p->number, p->name);
        for (i = 0; i < g_courses; ++i)
        {
            printf("%6.2f ", p->scores[i]);
            student_total += p->scores[i];
            course_total[i] += p->scores[i];
        }
        if (student_average)
        {
            printf("%6.2f %6.2f", student_total, student_total / g_courses);
        }
        printf("\n");
    }
    if (course_average)
    {
        printf("-------------------------------------------------------------------------\n");
        printf("%15s %15s ", "course", "total");
        for (i = 0; i < g_courses; ++i)
        {
            printf("%6.2f ", course_total[i]);
        }
        printf("\n%15s %15s ", "course", "average");
        for (i = 0; i < g_courses; ++i)
        {
            printf("%6.2f ", course_total[i] / clist_size(pcl));
        }
        printf("\n");
    }
    printf("-------------------------------------------------------------------------\n");

    free(course_total);
}

void show_header(flag_t student_average)
{
    int i = 0;
    printf("%15s %15s ", "number", "name");
    for (i = 0; i < g_courses; ++i)
    {
        printf("%5s%d ", "c", i + 1);
    }
    if (student_average)
    {
        printf("%6s %6s", "total", "avg");
    }
    printf("\n-------------------------------------------------------------------------\n");
}

void show_single(pstudent p, flag_t student_average)
{
    int i = 0;
    float student_total = 0.0;
    printf("%15lu %15s ", p->number, p->name);
    for (i = 0; i < g_courses; ++i)
    {
        printf("%6.2f ", p->scores[i]);
        student_total += p->scores[i];
    }
    if (student_average)
    {
        printf("%6.2f %6.2f", student_total, student_total / g_courses);
    }
    printf("\n");
}

void sort_by_total_score(clist* pcl, flag_t ascending)
{
    ascending ?
        clist_sort(NULL, pcl, sort_ascend_by_total_score_process_handler) :
        clist_sort(NULL, pcl, sort_descend_by_total_score_process_handler);
    show_all(pcl, false, true);
}
flag_t sort_descend_by_total_score_process_handler(const void* const data1, const void* const data2)
{
    int i = 0;
    pstudent p1 = (pstudent)data1;
    pstudent p2 = (pstudent)data2;
    float student_total[] = {0.0, 0,0};
    for (; i < g_courses; ++i)
    {
        student_total[0] += p1->scores[i];
        student_total[1] += p2->scores[i];
    }
    if (student_total[1] > student_total[0])
    {
        return true;
    }
    else
    {
        return false;
    }
}
flag_t sort_ascend_by_total_score_process_handler(const void* const data1, const void* const data2)
{
    int i = 0;
    pstudent p1 = (pstudent)data1;
    pstudent p2 = (pstudent)data2;
    float student_total[] = { 0.0, 0,0 };
    for (; i < g_courses; ++i)
    {
        student_total[0] += p1->scores[i];
        student_total[1] += p2->scores[i];
    }
    if (student_total[1] < student_total[0])
    {
        return true;
    }
    else
    {
        return false;
    }
}

void sort_by_number(clist* pcl)
{
    clist_sort(NULL, pcl, sort_ascend_by_number_process_handler);
    show_all(pcl, false, false);
}
flag_t sort_ascend_by_number_process_handler(const void* const data1, const void* const data2)
{
    pstudent p1 = (pstudent)data1;
    pstudent p2 = (pstudent)data2;
    if (p1->number > p2->number)
    {
        return true;
    }
    else
    {
        return false;
    }
}

void sort_by_name(clist* pcl)
{
    clist_sort(NULL, pcl, sort_by_name_process_handler);
    show_all(pcl, false, false);
}
flag_t sort_by_name_process_handler(const void* const data1, const void* const data2)
{
    pstudent p1 = (pstudent)data1;
    pstudent p2 = (pstudent)data2;
    if (strcmp(p1->name, p2->name) > 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}

void search_by_number(clist* pcl)
{
    unsigned long number;
    clist_iterator_t it = NULL;
    pstudent p = NULL;
    printf("Please input the number of student you want search:");
    scanf("%lu", &number);

    show_header(false);
    while (1) {
        it = clist_find(it, pcl, search_by_number_process_handler, (void*)number);
        if (it == clist_iterator_end)
        {
            break;
        }
        p = clist_get(it);
        show_single(p, false);
        it = clist_step(it, increase, pcl);
    }
}
flag_t search_by_number_process_handler(const void* const data1, const void* const data2)
{
    pstudent p = (pstudent)data1;
    if ((unsigned long)data2 == p->number)
        return true;
    else
        return false;
}

void search_by_name(clist* pcl)
{
    char name[15];
    clist_iterator_t it = NULL;
    pstudent p = NULL;
    printf("Please input the name of student you want search:");
    scanf("%s", name);

    show_header(false);
    while (1) {
        it = clist_find(it, pcl, search_by_name_process_handler, (void*)name);
        if (it == clist_iterator_end) {
            break;
        }
        p = clist_get(it);
        show_single(p, false);
        it = clist_step(it, increase, pcl);
    }
}
flag_t search_by_name_process_handler(const void* const data1, const void* const data2)
{
    pstudent p = (pstudent)data1;
    if (strcmp((char*)data2, p->name) == 0)
        return true;
    else
        return false;
}

void statistic_analysis_courses(clist* pcl)
{
    int i = 0, total_students = 0;
    pstudent p = NULL;
    clist_iterator_t it = NULL;

    int **statistic = (int**)malloc(sizeof(int*) * g_courses);
    for (; i < g_courses; ++i)
    {
        statistic[i] = (int*)malloc(sizeof(int) * 5);
        memset(statistic[i], 0, sizeof(int) * 5);
    }

    total_students = clist_size(pcl);
    for (it = clist_begin(pcl); it && it != clist_iterator_end; it = clist_step(it, increase, pcl))
    {
        p = clist_get(it);
        for (i = 0; i < g_courses; ++i)
        {
            if (p->scores[i] - 89 > 0) statistic[i][0]++;
            else if (p->scores[i] - 79 > 0) statistic[i][1]++;
            else if (p->scores[i] - 69 > 0) statistic[i][2]++;
            else if (p->scores[i] - 59 > 0) statistic[i][3]++;
            else statistic[i][4]++;
        }
    }

    printf("%10s %10s %10s %10s %10s %10s\n", "courses", "excellent", "good", "mean", "passed", "failed");
    printf("-------------------------------------------------------------------------\n");
    for (i = 0; i < g_courses; ++i)
    {
        printf("%9s%d %2d(%5.2f%%) %2d(%5.2f%%) %2d(%5.2f%%) %2d(%5.2f%%) %2d(%5.2f%%)\n", "c", i + 1,
            statistic[i][0], 100.0 * statistic[i][0] / total_students,
            statistic[i][1], 100.0 * statistic[i][1] / total_students,
            statistic[i][2], 100.0 * statistic[i][2] / total_students,
            statistic[i][3], 100.0 * statistic[i][3] / total_students,
            statistic[i][4], 100.0 * statistic[i][4] / total_students);
    }

    for (i = 0; i < g_courses; ++i)
    {
        free(statistic[i]);
    }
    free(statistic);
}

void write_to_file(clist* pcl, const char* filename)
{
    size_t ret = 0;
    pstudent p = NULL;
    clist_iterator_t it = NULL;

    FILE* file = fopen(filename, "w+b");
    if (!file)
    {
        printf("Failed to write file <%s>\n", filename);
        return;
    }

    fwrite(&g_courses, 1, sizeof(g_courses), file);
    for (it = clist_begin(pcl); it && it != clist_iterator_end; it = clist_step(it, increase, pcl))
    {
        p = clist_get(it);
        fwrite(p, 1, sizeof(student), file);
        fwrite(p->scores, 1, sizeof(float) * g_courses, file);
    }

    fclose(file);
}

void read_from_file(clist* pcl, const char* filename)
{
    size_t ret = 0;
    int courses = g_courses;
    pstudent p = NULL;
    clist_iterator_t it = NULL;

    FILE* file = fopen(filename, "r");
    if (!file)
    {
        printf("Failed to read file <%s>\n", filename);
        return;
    }

    ret = fread(&g_courses, 1, sizeof(g_courses), file);
    if (ret != sizeof(g_courses))
    {
        printf("Failed to read courses from file <%s>!\n", filename);
        fclose(file);
        return;
    }
    if (courses && courses != g_courses)
    {
        printf("You have input record with different courses between file <%s>!\n", filename);
        g_courses = courses; // rollback
        fclose(file);
        return;
    }

    while (!feof(file))
    {
        p = (pstudent)malloc(sizeof(student));
        ret = fread(p, 1, sizeof(student), file);
        if (ret != sizeof(student))
        {
            //printf("Some error occur on read file 1<%s>\n", filename);
            free(p);
            break;
        }

        p->scores = (float *)malloc(sizeof(float) * g_courses);
        ret = fread(p->scores, 1, sizeof(float) * g_courses, file);
        if (ret != sizeof(float) * g_courses)
        {
            //printf("Some error occur on read file 2<%s>\n", filename);
            free(p->scores);
            free(p);
            break;
        }

        clist_add(pcl, p, false);
    }

    fclose(file);
}

